package gui;

import java.awt.Frame;
import java.awt.Point;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.text.BadLocationException;

import logic.CommandRecaller;
import logic.Parser;

//@author A0100965J
public class KeyMonitor implements KeyListener {
	private Parser _parser;
	private JScrollPane _display;
	private JTextField _cmdLine;
	private SuggestionPanel _suggestion = null;
	private AutoComplete _autoComplete = null;
	private CommandRecaller _recaller;
	private String _currentCmd;

	private final int SCROLL_STEP = 50;
	private final String EMPTY_STRING = "";

	public KeyMonitor(JScrollPane scrollableDisplay, JTextField cmdLine, Parser parser) {
		_parser = parser;
		_display = scrollableDisplay;
		_cmdLine = cmdLine;
		_recaller = CommandRecaller.getInstance();
		_currentCmd = EMPTY_STRING;
	}

	public void keyPressed(KeyEvent e) {
		pressEvent(e);
	}

	public void keyReleased(KeyEvent e) {
		releaseEvent(e);
	}

	public void keyTyped(KeyEvent e) {
	}

	/**
	 * Keep track of the content in the command line and suggestion popup <br/>
	 * 
	 * @param e
	 *            A key pressed by the user
	 */
	private void releaseEvent(KeyEvent e) {
		int key = e.getKeyCode();
		switch (key) {
		case KeyEvent.VK_ENTER:
			if (!_cmdLine.getText().equals(EMPTY_STRING)) {
				sendToParser();
			}
			break;

		case KeyEvent.VK_TAB:
			selectSuggestion();
			break;

		case KeyEvent.VK_ESCAPE:
			MainConsole.getInstance().setState(Frame.ICONIFIED);
			break;

		case KeyEvent.VK_DOWN:
			if (_suggestion != null) {
				_suggestion.moveDown();
			} else {
				nextCommandInRecaller();
			}
			break;

		case KeyEvent.VK_UP:
			if (_suggestion != null) {
				_suggestion.moveUp();
			} else {
				previousCommandInRecaller();
			}
			break;

		case KeyEvent.VK_BACK_SPACE:
			if (!_cmdLine.getText().equals(EMPTY_STRING)) {
				showSuggestion(false);
			} else {
				setSuggestionNull();
			}
			break;

		case KeyEvent.VK_F10:
			MainConsole console = MainConsole.getInstance();
			toggleBtwStartAndHelp(console);
			break;

		default:
			newCharacterEntered(e);
			break;
		}
	}

	/**
	 * Action taken when the user types a new character in the command line
	 * 
	 * @param e
	 *            The character typed
	 */
	private void newCharacterEntered(KeyEvent e) {
		char newKey = e.getKeyChar();

		if (newKey != KeyEvent.CHAR_UNDEFINED && !_cmdLine.getText().equals(EMPTY_STRING)) {
			showSuggestion(true);
			if (newKey != '-') {
				setAutoCompletionNull();
			}
		}
		_currentCmd = _cmdLine.getText();
	}

	private void toggleBtwStartAndHelp(MainConsole console) {
		if (!console._isAtHelpPage) {
			console.showHelpPage();
		} else {
			console.clear();
			console._isAtHelpPage = false;
		}
	}

	/**
	 * Recall the latest command before the current pointer
	 */
	private void previousCommandInRecaller() {
		String newCmd = _recaller.recallPreviousCommand();
		if (newCmd != null) {
			_cmdLine.setText(newCmd);
		}
	}

	/**
	 * Recall the next command after the current pointer
	 */
	private void nextCommandInRecaller() {
		String newCmd = _recaller.recallNextCommand();
		if (newCmd != null) {
			_cmdLine.setText(newCmd);
		} else {
			_cmdLine.setText(_currentCmd);
		}
	}

	/**
	 * Either select a suggested word in the suggestion pop-up or confirm the
	 * auto-completed keywords
	 */
	private void selectSuggestion() {
		if (_suggestion != null) {
			_suggestion.insertSelection();
			setSuggestionNull();
		} else if (_autoComplete != null) {
			_autoComplete.insertAutoCompletion();
			setAutoCompletionNull();
		}
	}

	/**
	 * Send the current text in the command line to the parser for processing
	 */
	private void sendToParser() {
		_recaller.resetPointer();
		_parser.parseCommand(_cmdLine.getText());
		_currentCmd = EMPTY_STRING;
		_cmdLine.setText(EMPTY_STRING);
		if (_suggestion != null) {
			setSuggestionNull();
		}
	}

	/**
	 * Keep track of event where the user holds a non-character key
	 * 
	 * @param e
	 */
	private void pressEvent(KeyEvent e) {
		int key = e.getKeyCode();

		switch (key) {
		case KeyEvent.VK_PAGE_UP:
			scrollUp();
			break;

		case KeyEvent.VK_PAGE_DOWN:
			scrollDown();
			break;

		case KeyEvent.VK_F1:
			// Reduce window size
			MainConsole.getInstance().setWindowSize(false);
			break;

		case KeyEvent.VK_F2:
			// Increase window size
			MainConsole.getInstance().setWindowSize(true);
			break;

		default:
			break;
		}

	}

	private void scrollDown() {
		int height = _display.getVerticalScrollBar().getValue();
		if (height < _display.getVerticalScrollBar().getMaximum() - (SCROLL_STEP - 1)) {
			_display.getViewport().setViewPosition(new Point(0, height + SCROLL_STEP));
		} else {
			_display.getViewport().setViewPosition(new Point(0, _display.getVerticalScrollBar().getMaximum()));
		}
	}

	private void scrollUp() {
		int height = _display.getVerticalScrollBar().getValue();
		if (height > SCROLL_STEP - 1) {
			_display.getViewport().setViewPosition(new Point(0, height - SCROLL_STEP));
		} else {
			_display.getViewport().setViewPosition(new Point(0, 0));
		}
	}

	/**
	 * Search for word suggestion for a half-typed word <br/>
	 * and display either as a suggestion popup or auto-completion <br/>
	 * if there is any suggestion <br/>
	 * 3 cases: 1. when cursor is at the first word of the line (command word)
	 * 2. when cursor is at a word with '-' 3. others
	 */
	private void showSuggestion(boolean showAutoCompletion) {
		String subWord = EMPTY_STRING;
		String cmd = _cmdLine.getText();
		setSuggestionNull();
		int cursor = _cmdLine.getCaretPosition();
		int start = Math.max(0, cursor - 1);
		Point location;
		boolean isCommandWord = false;

		try {
			location = _cmdLine.modelToView(cursor).getLocation();
		} catch (BadLocationException e2) {
			return;
		}

		if (isCommandWord(start)) {
			// Case 1: command word
			subWord = cmd.substring(0, cursor).trim();
			isCommandWord = true;
		} else {
			while (start > 0) {
				if (!Character.isWhitespace(cmd.charAt(start))) {
					// Move the cursor backwards until it reaches a space
					start--;
				} else {
					if (start + 1 > cursor) {
						return;
					}
					if (start + 1 < cursor && isKeyWord(start + 1)) {
						// Case 2: keywords containing '-'
						start++;
						subWord = cmd.substring(start, cursor);
						if (subWord.equals("-") && showAutoCompletion == true) {
							_autoComplete = new AutoComplete(_cmdLine);
							return;
						}
						break;
					} else if (isCommandWord(start - 1) || isKeyWord(start - 1)) {
						// Case 3: part of a task information
						start++;
						subWord = cmd.substring(start, cursor);
						break;
					} else {
						start--;
					}
				}
			}
		}

		if (subWord.length() < 1) {
			return;
		}

		_suggestion = new SuggestionPanel(this, subWord, location, isCommandWord);

		_cmdLine.requestFocusInWindow();

		if (_suggestion.isEmpty()) {
			setSuggestionNull();
		}
	}

	/**
	 * Check if a word is a keyword by checking if the word contains '-' x is
	 * either at the start or end of the word
	 * 
	 * @param x
	 * @return
	 */
	private boolean isKeyWord(int x) {
		String cmd = _cmdLine.getText();
		int cursor = _cmdLine.getCaretPosition();
		int start = x;
		int end = x;
		while (!Character.isWhitespace(cmd.charAt(start))) {
			start--;
		}
		if (start != x) {
			start++;
		}
		while (end != cursor && !Character.isWhitespace(cmd.charAt(end))) {
			end++;
		}
		String word = cmd.substring(start, end);
		if (word.contains("-")) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Check if a word is a command word by checking if there is any space
	 * between index 0 and x
	 */
	private boolean isCommandWord(int x) {
		String cmd = _cmdLine.getText();
		String word = cmd.substring(0, x + 1).replaceAll("^\\s+", EMPTY_STRING);
		if (word.contains(" ")) {
			return false;
		} else {
			return true;
		}
	}

	/**
	 * Set suggestion to null
	 */
	public void setSuggestionNull() {
		if (_suggestion != null) {
			_suggestion.hide();
			_suggestion = null;
		}
	}

	/**
	 * Set autoComplete to null
	 */
	public void setAutoCompletionNull() {
		if (_autoComplete != null) {
			_autoComplete = null;
		}
	}

	/**
	 * get the current content in the command line
	 * 
	 * @return cmd
	 */
	public JTextField getCommand() {
		return _cmdLine;
	}
}
